package com.cirkana.viewer.route.model.gpx;

import java.io.InputStream;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class GpxTrackerParser {

	public static Tracker getTracker(InputStream inputStream)
			throws GpxTrackerException {
		Document document = loadGPX(inputStream);
		Tracker tracker = new Tracker();
		tracker.setMetadata(extractMetadata(document));
		tracker.setWayPoints(extractWayPoints(document));
		tracker.setTracks(extractTracks(document));
		return tracker;
	}

	private static Metadata extractMetadata(Document document) {

		Metadata metadata = RouteController.createMetadata();

		NodeList metadataNodeList = document.getElementsByTagName("metadata");
		if (metadataNodeList.getLength() > 0) {
			Node metadataNode = metadataNodeList.item(0);

			NodeList metadataNodes = metadataNode.getChildNodes();
			for (int iii = 0; iii < metadataNodes.getLength(); iii++) {
				Node node = metadataNodes.item(iii);

				if ("name".equals(node.getNodeName()))
					metadata.setName(node.getTextContent());

				if ("desc".equals(node.getNodeName()))
					metadata.setDesc(node.getTextContent());

				if ("author".equals(node.getNodeName())) {

					NodeList authorNodes = node.getChildNodes();
					for (int jjj = 0; jjj < authorNodes.getLength(); jjj++) {
						Node authorNode = authorNodes.item(iii);

						if ("name".equals(authorNode.getNodeName()))
							metadata.setAuthor(authorNode.getTextContent());
					}
				}

				if ("preview".equals(node.getNodeName()))
					metadata.setPreview(node.getTextContent());
			}
		}

		return metadata;
	}

	private static ArrayList<WayPoint> extractWayPoints(Document document) {
		ArrayList<WayPoint> wayPoints = new ArrayList<WayPoint>();

		NodeList wpts = document.getElementsByTagName("wpt");
		for (int iii = 0; iii < wpts.getLength(); iii++) {
			Node wpt = wpts.item(iii);

			WayPoint wayPoint = RouteController.createWayPoint();
			wayPoint.setLat(Double.parseDouble(wpt.getAttributes()
					.getNamedItem("lat").getNodeValue()));
			wayPoint.setLon(Double.parseDouble(wpt.getAttributes()
					.getNamedItem("lon").getNodeValue()));

			NodeList wptNodes = wpt.getChildNodes();
			for (int jjj = 0; jjj < wptNodes.getLength(); jjj++) {
				Node node = wptNodes.item(jjj);
				if ("ele".equals(node.getNodeName()))
					wayPoint.setEle(Double.parseDouble(node.getTextContent()));

				// TOOD: Falten dates
				// if ("time".equals(node.getNodeName()))
				// wayPoint.setTime(dateFormatter.parse(node.getTextContent()));

				if ("name".equals(node.getNodeName()))
					wayPoint.setName(node.getTextContent());
				if ("cmt".equals(node.getNodeName()))
					wayPoint.setCmt(extractInnerHtml(node));
				if ("desc".equals(node.getNodeName()))
					wayPoint.setDesc(extractInnerHtml(node));
			}
			wayPoints.add(wayPoint);
		}
		return wayPoints;
	}

	private static ArrayList<Track> extractTracks(Document document) {
		ArrayList<Track> tracks = new ArrayList<Track>();

		NodeList trks = document.getElementsByTagName("trk");
		for (int iii = 0; iii < trks.getLength(); iii++) {
			Node trk = trks.item(iii);

			Track track = RouteController.createTrack();

			NodeList trkNodes = trk.getChildNodes();
			for (int jjj = 0; jjj < trkNodes.getLength(); jjj++) {
				Node node = trkNodes.item(jjj);

				if ("name".equals(node.getNodeName()))
					track.setName(node.getTextContent());
				if ("cmt".equals(extractInnerHtml(node)))
					track.setCmt(node.getTextContent());
				if ("desc".equals(node.getNodeName()))
					track.setDesc(extractInnerHtml(node));
				if ("trkseg".equals(node.getNodeName()))
					track.setTrackPoints(extractTrackPoints(node));
			}
			tracks.add(track);
		}

		return tracks;
	}

	private static ArrayList<TrackPoint> extractTrackPoints(Node trkseg) {
		ArrayList<TrackPoint> trackPoints = new ArrayList<TrackPoint>();

		NodeList trkpts = trkseg.getChildNodes();
		for (int iii = 0; iii < trkpts.getLength(); iii++) {
			Node trkpt = trkpts.item(iii);

			if ("trkpt".equals(trkpt.getNodeName())) {

				TrackPoint trackPoint = RouteController.createTrackPoint();
				trackPoint.setLat(Double.parseDouble(trkpt.getAttributes()
						.getNamedItem("lat").getNodeValue()));
				trackPoint.setLon(Double.parseDouble(trkpt.getAttributes()
						.getNamedItem("lon").getNodeValue()));

				NodeList trkptNodes = trkpt.getChildNodes();
				for (int jjj = 0; jjj < trkptNodes.getLength(); jjj++) {
					Node node = trkptNodes.item(jjj);

					// TOOD: Falten dates

					if ("ele".equals(node.getNodeName()))
						trackPoint.setEle(Double.parseDouble(node
								.getTextContent()));
				}
				trackPoints.add(trackPoint);
			}
		}
		return trackPoints;
	}

	private static Document loadGPX(InputStream is) throws GpxTrackerException {
		DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
		DocumentBuilder build;
		try {
			build = fact.newDocumentBuilder();
			return build.parse(is);
		} catch (Exception e) {
			throw new GpxTrackerException("loadGPX()", e);
		}
	}

	private static String extractInnerHtml(Node node) {
		StringBuilder s = new StringBuilder();

		NodeList nodeList = node.getChildNodes();
		for (int iii = 0; iii < nodeList.getLength(); iii++) {
			Node child = nodeList.item(iii);

			if ("#text".equals(child.getNodeName()))
				s.append(child.getNodeValue());

			else if (!"#comment".equals(child.getNodeName())) {

				s.append("<");
				s.append(child.getNodeName());
				NamedNodeMap attributes = child.getAttributes();
				for (int jjj = 0; jjj < attributes.getLength(); jjj++) {
					Node attr = attributes.item(jjj);
					s.append(" ");
					s.append(attr.getNodeName());
					s.append("=\"");
					s.append(attr.getNodeValue());
					s.append("\"");
				}
				s.append(">");
				s.append(extractInnerHtml(child));
				s.append("</");
				s.append(child.getNodeName());
				s.append(">");
			}

		}

		return s.toString();
	}
}
